#include "tipo_struct.h"

TipoStruct::TipoStruct(bool obligatorio):TipoDatoCompuesto(obligatorio){}

TipoStruct::~TipoStruct(){
}

bool TipoStruct::mismo_tipo(const TipoDato& dato) const{
	bool mismo_tipo = TipoDato::mismo_tipo(dato);
	if ((this->get_tamanio()) != (dato.get_tamanio()))
		mismo_tipo = false;
	else {
		std::vector< TipoDato* >::const_iterator it = elementos.begin();
		unsigned int cont = 0;
		TipoStruct* dato_struct = (TipoStruct*) &dato;
		while ((it != elementos.end()) && (mismo_tipo)) {
				TipoDato* aux = (*it);
				mismo_tipo = (aux->mismo_tipo((*dato_struct)[cont]));
				cont++;
				it++;
		}
	}
	return mismo_tipo;
}

TipoDato& TipoStruct::operator= (const TipoDato& dato) throw (ExcepErrorTipos) {
	if (this == &dato)
		return *this;
	if (!this->mismo_tipo(dato))
		throw ExcepErrorTipos("Error - Se esperaba tipo struct");
	
	std::vector< TipoDato* >::iterator it = elementos.begin();
	std::vector< TipoDato* >::iterator it2 = ((TipoStruct *)(&dato))->elementos.begin();
	while (it != elementos.end()) {
		TipoDato* copia = (*it);
		TipoDato* orig = (*it2);
		if ((copia != NULL) && (orig != NULL))	
			*copia = *orig;  
		it++;
		it2++;
	}
	this->obligatorio = dato.es_obligatorio();
	return *this;
}

unsigned int TipoStruct::get_tamanio() const{
	return elementos.size();
}

bool TipoStruct::operator== (const TipoDato& dato) const throw (ExcepErrorTipos) {
	if (!this->mismo_tipo(dato))
		throw ExcepErrorTipos("Error - Se esperaba tipo struct");
	
	const TipoDatoCompuesto * _dato = (TipoDatoCompuesto *) &dato;
	bool _return = TipoDatoCompuesto::elementos_iguales(_dato);
			
	return _return; 
}

bool TipoStruct::operator< (const TipoDato& dato) const throw (ExcepErrorTipos) {
	if (this->mismo_tipo(dato))
		throw ExcepErrorTipos("Error - Se esperaba tipo struct");
	
	const TipoDatoCompuesto * _dato = (TipoDatoCompuesto *) &dato;
	bool _return = TipoDatoCompuesto::elementos_menores(_dato);
			
	return _return;
}
bool TipoStruct::add_dato(const TipoDato& dato){
	TipoDato* aux = dato.clonar();
	elementos.push_back(aux);
	return true;
}
	 
bool TipoStruct::quitar_dato(unsigned int ind){
	return TipoDatoCompuesto::quitar_dato(ind);
}
	  
TipoDato& TipoStruct::operator[] (size_t indice) throw (ExcepIndiceDesbordado){
	return (TipoDatoCompuesto::operator[](indice));
}

CodTipo TipoStruct::get_codigo_tipo() const{
	return STRUCT;
}

TipoDato* TipoStruct::clonar() const{
	TipoStruct* result = new TipoStruct( es_obligatorio() );
	copiar_elementos(result);
	return result;	
}

ostream& TipoStruct::toStream(ostream& out) const{
	std::vector<TipoDato*>::const_iterator it = elementos.begin();
	while (it != elementos.end()){
		TipoDato* aux = (*it);	
		aux->toStream(out);
		it++;
		if (it != elementos.end())
			out << ',';
	}
	return out;
}

void TipoStruct::serializar(char ** retorno, unsigned int &tam){
	//arranca reservando 4 bytes para el campo del tamano total del
	//struct que es un int
	tam = sizeof(unsigned int);
	*retorno = new char[tam];
	
	unsigned int tam_aux;
	char * conv_aux;
	
	std::vector<TipoDato*>::const_iterator it = elementos.begin();
	while (it != elementos.end()){
		//convierto el tipo de dato
		(*it)->serializar(&conv_aux, tam_aux);
		//realoco la memoria para reservar mas espacio para lo nuevo.
		//Por ahora no reviso errores.
		
		char * aux_copia = new char[tam + tam_aux];
		memcpy(aux_copia, *retorno, tam);
		delete[] (*retorno);
		*retorno = aux_copia; 
		//copio el contenido en retorno
		memcpy(*retorno + tam, conv_aux, tam_aux);
		//incremento el espacio ocupado
		tam += tam_aux;
		//libero el auxiliar
		delete[] conv_aux;
		
		++it;
	}
	//guardo al comienzo el tamano total
	memcpy(*retorno, &tam, sizeof(unsigned int));

}
		 
bool TipoStruct::deserializar(char * serializados, unsigned int tam){
	if(tam > 4 ){
		int tam_total;
		//leo le tamano total que va a tener el contenido
		memcpy(&tam_total, serializados, sizeof(unsigned int));
		
		int pos = sizeof(unsigned int);
		
		char * aux;//aca guardo los trozos de la cadena a deserializar
		unsigned int tam_aux;
		
		for(unsigned int ind = 0; ind < elementos.size(); ind++){
			switch(elementos[ind]->get_codigo_tipo() ){
				case INT:	
					tam_aux = sizeof(int);
					aux = new char[tam_aux];
					memcpy(aux, serializados + pos, sizeof(int) );
					break;
				case STRING:
					//saco el tamano del string
					unsigned short int tam_string;
					memcpy(&tam_string, serializados + pos, sizeof(unsigned short int));
					//copio eso mas el tag a aux
					tam_aux = tam_string + sizeof(unsigned short int);
					aux = new char[tam_aux];
					memcpy(aux, serializados + pos, tam_aux );
					break;
				case STRUCT:
					//saco el tamano del struct
					int tam_struct;
					memcpy(&tam_struct, serializados + pos, sizeof(unsigned int));
					//copio eso mas el tag a aux
					tam_aux = tam_struct; 
					aux = new char[tam_aux];
					memcpy(aux, serializados + pos, tam_aux );
					break;
				case COLECCION:
					//reviso el byte de presencia primero
					char esta_presente;
					memcpy(&esta_presente,serializados + pos, sizeof(char));
					if(esta_presente == 1 ){
						memcpy(&tam_aux, serializados + pos + 1 , sizeof(unsigned int) );
						aux = new char[tam_aux];
						memcpy(aux, serializados + pos, tam_aux );
					}
					else{
						aux = NULL;
						tam_aux = 1;
					}
					break;
			}
			if(aux != NULL){
				elementos[ind]->deserializar(aux, tam_aux);
				delete[] aux;
			}
			pos += tam_aux;
		}
		return true;
	}
	return false;
}
